﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using CloudReader.Net.Protocol;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CloudReader.Net.Client
{
    //@ PART:   CloudReaderClient
    //@ Author: 罗应良
    //@ Date:   2017/2/13

    public class CloudReaderClient : CloudReaderSocket
    {
        public static int ServerPort = 60200;

        DynamicBuffer m_buffer;
        SocketAsyncEventArgs read;
        SocketAsyncEventArgs write;
        SocketAsyncEventArgs Connect;
        IPEndPoint m_server,local;
        Parser parser;
        Mutex mutex;
        

        bool isLogin;
        byte[] cache;
        string m_user;
        string m_pw;
        int m_errorTime;

        public CloudReaderClient() : base()
        {
            Init();
        }
        void Init()
        {
            mutex = new Mutex();
            cache = new byte[4096];
            read = new SocketAsyncEventArgs();
            read.SetBuffer(cache, 0, cache.Length);
            write = new SocketAsyncEventArgs();
            Connect = new SocketAsyncEventArgs();
            m_buffer = new DynamicBuffer();
            m_buffer.RecieveCompleted += (data) => { OnNewMessage?.Invoke(data); };
            parser = new Parser();
            m_server = null;
            isLogin = false;
            m_errorTime = 0;
            base.SetDefault(0);
        }

        public void Login(IPAddress server, string user, string passwd)
        {
            if (m_server == null)
                m_server = new IPEndPoint(server, ServerPort);
            m_user = user;
            m_pw = passwd;
            Connect.UserToken = m_socket;
            Connect.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);
            Connect.RemoteEndPoint = m_server;

            if (!m_socket.ConnectAsync(Connect))
            {
                m_socket.Connect(m_server);
            }
            local = m_socket.LocalEndPoint as IPEndPoint;
        }
        public void Send(CloudReaderPackage pack)
        {
            var data = Parser.Encode(pack);
            write.SetBuffer(data, 0, data.Length);
            if(m_socket.SendAsync(write))
            {
                var n = new NetworkStream(m_socket);
                n.Write(data,0,data.Length);
                n.Close();
            }
        }
        public event Action OnErrorConnection;
        public event Action OnLoginFailed;
        public event Action OnLoginSuccess;
        public event Action<CloudReaderPackage> OnNewMessage;
        public event Action CannotConnectServer;
        public void Close()
        {
            try
            {
                m_socket.Close();
            }
            catch
            {

            }
            m_buffer.Dispose();
        }

        #region 内部方法
        protected void ConnectRequest(SocketAsyncEventArgs e)
        {
            if (e != null && e.SocketError == SocketError.Success)
            {
                var s = e.UserToken as Socket;
                if (s.Connected)
                {
                    var str = $"{m_user}_{m_pw}";
                    var p = parser[str, true];
                    var data = Parser.Encode(p);

                    write.SetBuffer(data, 0, data.Length);
                    m_socket.SendAsync(write);

                    read.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);
                    read.UserToken = s;
                    m_socket.ReceiveAsync(read);
                }
                else
                {
                    OnErrorConnection?.Invoke();
                }
            }
            else
            {
                OnErrorConnection?.Invoke();
            }
        }
        protected void OnConnect(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    ConnectRequest(e);
                    break;
                case SocketAsyncOperation.Send:
                    OnSendCompleted(e);
                    break;
                case SocketAsyncOperation.Receive:
                    ProcessLoginReceive(null, e);
                    break;
            }
        }
        protected void ProcessLoginReceive(object sender, SocketAsyncEventArgs e)
        {
            if (e != null && e.SocketError == SocketError.Success)
            {
                var s = e.UserToken as Socket;
                if (s.Connected && e.BytesTransferred > 0)
                {
                    var data = new byte[e.BytesTransferred];
                    Array.Copy(e.Buffer, e.Offset, data, 0, data.Length);
                    var p = Parser.Decode(data);
                    if (p.Type == CloudReaderProtocolType.Validate)
                    {
                        if (p.Data == "1")
                        {
                            isLogin = true;
                            read.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                            write.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                            read.UserToken = s;
                            m_socket.ReceiveAsync(read);
                            Task t = new Task(new Action(KeepAlive));
                            t.Start();
                            OnLoginSuccess?.Invoke();
                        }
                        else
                        {
                            read.Completed -= OnConnect;
                            isLogin = false;             
                            OnLoginFailed?.Invoke();
                        }
                    }
                    else
                    {
                        if (m_errorTime < 5)
                        {
                            ConnectRequest(e);
                        }
                        else
                        {
                            CannotConnectServer?.Invoke();
                        }
                    }
                }
                else
                {
                    OnErrorConnection?.Invoke();
                }
            }
            else
            {
                OnErrorConnection?.Invoke();
            }
        }

        protected void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    OnReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    OnSendCompleted(e);
                    break;
            }
        }


        async protected void KeepAlive()
        {
            var p = parser[CloudReaderProtocolType.HeartBeat];
            var data = Parser.Encode(p);
            while (isLogin)
            {
                write.SetBuffer(data, 0, data.Length);
                m_socket.SendAsync(write);
                await Task.Delay(1000);
            }
        }
        protected void OnReceive(SocketAsyncEventArgs e)
        {
            if (e != null && e.SocketError == SocketError.Success)
            {
                var s = e.UserToken as Socket;
                if (e.BytesTransferred > 0)
                {
                    var data = new byte[e.BytesTransferred];
                    Array.Copy(e.Buffer, e.Offset, data, 0, data.Length);
                    m_buffer.Enqueue(data);
                }
            }
        }
        protected void OnSendCompleted(SocketAsyncEventArgs e)
        {
        }
        #endregion
    }
}
